package gu.simplemq;

import java.lang.reflect.Type;
import java.net.URI;
import java.util.Map;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableMap;
import com.google.common.reflect.TypeToken;

import gu.simplemq.json.BaseJsonEncoder;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;

public class MessageQueueFactorys implements Constant{

	private static volatile IMessageQueueFactory defaultFactory = null;
	private MessageQueueFactorys() {
	}
	/**
	 * 设置默认{@link IMessageQueueFactory}实例<br>
	 * 仅当默认实例未初始化(为{@code null})且输入参数不为{@code null}时有效(返回{@code true})
	 * 如果默认实例已经初始化,则输出警告日志,返回{@code false}
	 * @param defaultFactory 为{@code null}返回{@code false}
	 * @return  设置成功返回{@code true},否则返回{@code false}
	 */
	public static boolean setDefaultFactory(IMessageQueueFactory defaultFactory){
		if(MessageQueueFactorys.defaultFactory == null){
			synchronized (MessageQueueFactorys.class) {
				if(MessageQueueFactorys.defaultFactory == null){
					if(defaultFactory != null){
						checkArgument(defaultFactory.initialized(),"input defaultFactory is uninitialized");
						MessageQueueFactorys.defaultFactory = defaultFactory;
						return true;
					}else {
						logger.warn("input argument 'defaultFactory' is null");
						return false;
					}
				}
			}
		}
		logger.warn("INVALID INVOCATION,default factory was initialized already before this invocation");
		return false;
	}
	/**
	 * 返回默认实例,如果 {@link #defaultFactory}为null则抛出异常
	 * @return {@link IMessageQueueFactory}实例
	 * @throws NullPointerException 默认实例未初始化
	 */
	public static IMessageQueueFactory getDefaultFactory() {		
		return checkNotNull(defaultFactory,"defaultFactory is uninitialized");
	}
	/**
	 * 检测默认实例是否初始化
	 * @return 默认实例已经初始化则返回{@code true},否则返回false
	 */
	public static boolean defaultFactoryInitialized(){
		return defaultFactory != null;
	}
	public static ImmutableMap<MessageQueueType, IMessageQueueFactory> getFactories(){
		checkState(MQContextLoader.factories.size() > 0,"NOT FOUND instance of %s" ,IMessageQueueFactory.class.getName());
		return MQContextLoader.factories;
	}
	public static IMessageQueueFactory getFactory(String implType){
		return getFactory(MessageQueueType.valueOf(implType));
	}
	public static IMessageQueueFactory getFactory(MessageQueueType implType){
		return checkNotNull(getFactories().get(implType),
				"INVALID impltype %s,VALID VALUES:%s",implType,Joiner.on(".").join(getFactories().keySet()));
	}

	/**
	 * 根据输入的URI scheme,返回对应的{@link IMessageQueueFactory}实例<br>
	 * 目前支持的scheme有: {@value gu.simplemq.Constant#MQTT_SCHEMA},
	 * {@value gu.simplemq.Constant#MQTT_SSL_SCHEMA}, {@value gu.simplemq.Constant#AMQP_SCHEMA},
	 * {@value gu.simplemq.Constant#REDIS_SCHEMA}, {@value gu.simplemq.Constant#STOMP_SCHEMA},
	 * {@value gu.simplemq.Constant#OPENWIRE_SCHEMA}<br>
	 * 输入参数为null或空字符串时抛出异常
	 * 
	 * @param uriSchema URI scheme
	 * @return {@link IMessageQueueFactory}实例,如果没有找到则抛出异常
	 * @since 2.4.0
	 */
	public static IMessageQueueFactory getFactoryByUriScheme(String uriSchema) {
		checkArgument(uriSchema != null && !uriSchema.isEmpty(), "uriSchema is null or empty");
		for (IMessageQueueFactory factory : getFactories().values()) {
			if (factory.getSupportedUriSchemes().contains(uriSchema)) {
				return factory;
			}
		}
		throw new IllegalArgumentException("NOT FOUND factory for uri scheme:" + uriSchema);
	}

	/**
	 * 根据输入的协议类型,返回对应的{@link IMessageQueueFactory}实例<br>
	 * 目前支持的协议类型有: {@value gu.simplemq.Constant#MQTT_SCHEMA}, {@value gu.simplemq.Constant#AMQP_SCHEMA},
	 * {@value gu.simplemq.Constant#REDIS_SCHEMA}, {@value gu.simplemq.Constant#STOMP_SCHEMA},
	 * {@value gu.simplemq.Constant#OPENWIRE_SCHEMA}<br>
	 * 输入参数为null或空字符串时抛出异常
	 * 
	 * @param protocol 协议类型
	 * @return {@link IMessageQueueFactory}实例,如果没有找到则抛出异常
	 * @since 2.4.0
	 */
	public static IMessageQueueFactory getFactoryByProtocol(String protocol) {
		checkArgument(protocol != null && !protocol.isEmpty(), "protocol is null or empty");
		for (IMessageQueueFactory factory : getFactories().values()) {
			if (factory.getProtocol().equals(protocol)) {
				return factory;
			}
		}
		throw new IllegalArgumentException("NOT FOUND factory for protocol:" + protocol);
	}

	/**
	 * 根据输入的{@link Map}中 {@value gu.simplemq.Constant#MQ_URI}
	 * 字段的scheme,返回对应的{@link IMessageQueueFactory}实例，并使用{@link Map}初始化该实例<br>
	 * 目前支持的协议类型有:
	 * <ul>
	 * <li>{@value gu.simplemq.Constant#AMQP_SCHEMA}
	 * <li>{@value gu.simplemq.Constant#OPENWIRE_SCHEMA}
	 * <li>{@value gu.simplemq.Constant#STOMP_SCHEMA}
	 * <li>{@value gu.simplemq.Constant#MQTT_SCHEMA}
	 * <li>{@value gu.simplemq.Constant#MQTT_SSL_SCHEMA}
	 * <li>{@value gu.simplemq.Constant#REDIS_SCHEMA}
	 * </ul>
	 * 比如:
	 * 
	 * <pre>
	 * Map<String, Object> connParams = new HashMap<>();
	 * connParams.put(MQ_URI, "mqtt://localhost:1883");
	 * IMessageQueueFactory factory = MessageQueueFactorys.getInitializedFactoryByUri(connParams);
	 * </pre>
	 * 
	 * 输入参数为null或空字符串,或输入参数没有定义"uri" 字段时则抛出异常<br>
	 * 如果输入参数为 {@value gu.simplemq.Constant#MQ_URI} 字段的scheme不在支持的协议类型中,则抛出异常<br>
	 * 如果 scheme 对应的 {@link IMessageQueueFactory} 实例已经初始化,则抛出异常<br>
	 * 
	 * @param connParams              {@link Map}实例,包含'uri'参数
	 * @param skipInitIfInititialized 当 {@link IMessageQueueFactory}
	 *                                实例已经初始化时是否跳过初始化过程, 如果为 {@code true} 则直接返回
	 *                                {@link IMessageQueueFactory} 实例,否则执行初始化过程
	 * @return {@link IMessageQueueFactory}实例,如果没有找匹配协议类型的实例,则抛出异常
	 * @since 2.4.0
	 * @see #getFactoryByUriScheme(String)
	 * @see IMessageQueueFactory#init(Map)
	 */
	@SuppressWarnings("rawtypes")
	public static IMessageQueueFactory getInitializedFactoryByUriScheme(Map connParams,
			boolean skipInitIfInititialized) {
		checkArgument(connParams != null && !connParams.isEmpty(), "connParams is null or empty");
		Object v = connParams.get(MQ_URI);
		URI uri;
		if (v instanceof URI) {
			uri = (URI) v;
		} else if (v instanceof String) {
			uri = URI.create((String) v);
		} else {
			throw new IllegalArgumentException("INVALID connParams,uri is null or not a string or URI");
		}
		String scheme = uri.getScheme();
		if (scheme == null || scheme.isEmpty()) {
			throw new IllegalArgumentException("INVALID 'uri' parameter,scheme is null :" + uri.toString());
		}
		IMessageQueueFactory factory = getFactoryByUriScheme(scheme);
		if (skipInitIfInititialized) {
			if(factory.initialized()){
				return factory;
			}else{
				logger.warn("factory for uri scheme:{} was not initialized,try to initialize it",scheme);
			}
		}
		return factory.init(connParams);
	}

	/**
	 * 根据输入的JSON字符串中 {@value gu.simplemq.Constant#MQ_URI}
	 * 字段的scheme,返回对应的{@link IMessageQueueFactory}实例，并使用{@link Map}初始化该实例<br>
	 * 
	 * @param json                    JSON字符串
	 * @param skipInitIfInititialized 当 {@link IMessageQueueFactory}
	 *                                实例已经初始化时是否跳过初始化过程, 如果为 {@code true} 则直接返回
	 *                                {@link IMessageQueueFactory} 实例,否则执行初始化过程
	 * @return {@link IMessageQueueFactory}实例,如果没有找匹配协议类型的实例,则抛出异常
	 * @since 2.4.0
	 * @see #getInitializedFactoryByUriScheme(Map, boolean)
	 */
	public static IMessageQueueFactory getInitializedFactoryByUriScheme(String json, boolean skipInitIfInititialized) {
		return getInitializedFactoryByUriScheme(asMQConnParam(json), skipInitIfInititialized);
	}

	public static Map<String,Object> asMQConnParam(String json){
		@SuppressWarnings("serial")
		Type paramType = new TypeToken<Map<String, Object>>(){}.getType();
		return BaseJsonEncoder.getEncoder().fromJson(json, paramType);
	}
	public static Map<String,String> asMQConnParam2(String json){
		@SuppressWarnings("serial")
		Type paramType = new TypeToken<Map<String, String>>(){}.getType();
		return BaseJsonEncoder.getEncoder().fromJson(json, paramType);
	}
	/**
	 * 关闭所有消息系统(message queue)工厂接口实例
	 */
	public static void closeAllFactories(){
		for(IMessageQueueFactory factory:getFactories().values()){
			try {
				factory.close();
			} catch (Throwable e) {
				e.printStackTrace();
			}
		}
	}
}
